基本概念
React.js
React.js 是 React 的核心库,在应用中必须先加载核心库。
ReactDOM.js
ReactDOM.js 是 React 的 DOM 渲染器,React 将核心库和渲染器分离开了,为了在 web 页面中显示开发的组件,需要调用 ReactDOM.render 方法, 第一个参数是 React 组件,第二个参数为 HTMLElement
JSX
组件
组件是 React 中的核心概念,页面当中的所有元素都是通过 React 组件来表达, 我们将要写的 React 代码绝大部分都是在做 React 组件的开发
VIRTUAL DOM
React 抽象出来的虚拟 DOM 树 (其实就是内存中的一个js 对象),虚拟树是 React 高性能的关键。
单向数据流:one-way reactive data flow
React 应用的核心设计模式,数据流向自顶向下
react 工作原理
- state,props 数据
- jsx 模版
- 数据+ 模版 生成虚拟 DOM(就是一个 js 对象,用来描述真实 DOM) eg: { ‘div’, { id : ‘abc’ }, [‘span’,{},’hello’]}
- 用虚拟 DOM 结构生成真实 DOM,来显示 eg : hello
- state 发生变化
- 数据+模版 生成 新的 虚拟 DOM eg : { ‘div’, { id : ‘abc’ }, [‘span’,{},’bye’]}
- 比较原始虚拟 DOM 和新虚拟 DOM 的区别 (用到 diff 算法,同层比对),找到区别 eg: span 中内容
- 直接操作 DOM, eg : 改变 span 中内容
组件生命周期
componentWillMount
- 条件:第一次渲染阶段在调用 render 方法前会被调用
- 作用:该方法在整个组件生命周期只会被调用一次,所以可以利用该方法做一些组件内部的初始化工作
componentDidMount
- 条件:第一次渲染成功过后,组件对应的 DOM 已经添加到页面后调用
- 作用:此时组件对应的 DOM 已经存在,我们可以在这个时候做一些依赖 DOM 的操作或者其他的一些如请求数据,和第三方库整合的操作。如果嵌套了子组件,子组件会比父组件优先渲染,所以这个时候可以获取子组件对应的 DOM
componentWillReceiveProps(newProps)
- 条件:当组件获取新属性的时候,第一次渲染不会调用
- 作用:这个时候可以根据新的属性来修改组件状态
1 | componentWillReceiveProps: function(nextProps) { |
- 注意:这个时候虽说是获取新属性,但并不能确定属性一定改变了,例如一个组件被多次渲染到 DOM 中
1 | eg:var Component = React.createClass({ |
shouldComponentUpdate(nextProps, nextState)
- 条件:接收到新属性或者新状态的时候在 render 前会被调用(除了调用 forceUpdate 和初始化渲染以外)
- 作用:该方法让我们有机会决定是否重渲染组件,如果返回 false,那么不会重渲染组件,借此可以优化应用性能
componentWillUpdate(nextProps, nextState)
- 条件:当组件确定要更新,在 render 之前调用
- 作用:这个时候可以确定一定会更新组件,可以执行更新前的操作
- 注意:方法中不能使用 setState ,setState 的操作应该在 componentWillReceiveProps 方法中调用
componentDidUpdate(prevProps, prevState)
- 条件:更新被应用到 DOM 之后
- 作用: 可以执行组件更新过后的操作
React 与 DOM
获取 DOM 元素
从组件的生命周期中,Dom 真正添加到 html 中 hook 是,
- componentDidMount
- componentDidUpdate
这两个函数中可以获取到,react 提供了两种获取方式
findDOMNode()
1
2
3
4
5
6
7
8
9var MyComponent = React.createClass({
render: function() {
return <div> .... </div>;
},
componentDidMount: function() {
var $root = ReactDOM.findDOMNode(this);
console.log($root);
}
});注意:此方法不能用到无状态组件上,且只能获取到 root 元素,(如果 dom 多层级,获取子级元素需要 refs)
Refs
每个组件都有 this.refs 属性,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var MyComponent = React.createClass({
render: function() {
return (
<div>
<button ref="btn">...</button>
<a href="" ref="link"></a>
</div>
);
},
componentDidMount: function() {
var $btn = this.refs.btn;
var $link = this.refs.link;
console.log($btn, $link);
}
});
redux
redux 的引入:不同组件之间的通讯时 由于 层级太深 导致麻烦, 所以引入 redux 的 Store 统一管理
redux = flux + reducers
redux 的工作流
描述 :
组件通过 dispatch 发起 action
- store 把之前的数据 state 和 actions 转发给 reducers
- reducers 根据 actions.type 操作 state,并返回新 state 到 store 中
- store 把 新 state 替换老 state
- store 发生改变 组件 会感知到改变,然后 setState ( 组件要注册 store.subscribe()监听)
代码中使用
安装
1 | npm install redux —save |
编写
src 下建 store 文件夹 =》 index.js state 中心
1
2
3
4
5import { createStore } from "redux";
//第二步创建的
import reducer from "./reducer.js";
const store = createStore(reducer);
export default store;store => reducer.js state 操作,管理中心
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//默认初始化的state 状态
const defaultState = {
inputValue: ""
};
export default (state = defaultState, action) => {
switch (action.type) {
case "changeInputValue":
return {
...state,
inputValue: action.value
};
break;
}
return state;
};组件中引入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import store form './store';
// 组件的 constructor 构造方法中
constructor(props){
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.state = store.getState();
//订阅store的改变 dispacth之后store改变
store.subscribe(()=>{
this.setState(store.getState());
});
}
//发起action
handleInputChange(value){
const actions = {
type : 'changeInputValue',
value: value
}
store.dispatch(actions);
}opt :
这个里面的 actions.type 可以统一管理, 在 src/store 建一个 actionsTypes.js 文件,然后去引用对应变量
1
export const CHANGE_INPUT_VALUE = "changeInputValue";
actions 在 组建中去创建太过分散,统一管理 ,在 src/store 建一个 actionsCreator.js 文件,
1
2
3
4
5
6import { CHANGE_INPUT_VALUE } from "./actionTypes";
//对应组件中使用这个去创建对应action
export const getInputChangeAction = value => ({
type: CHANGE_INPUT_VALUE,
value
});//组件中的 handleInputChange
handleInputChange(value){
const actions = getInputChangeAction(value)
store.dispatch(actions);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
**注意** :
- store 是唯一的 (整个项目就一个 createStore())
- 只有 store 才能改变 (reducer 中返回的 state 也是给到 store 中改变 state 的)
- reducer 函数必须是纯函数,(固定输入,就有固定输出,不能有异步操作等,)
复习:四个函数 createStore() ; getState() ; dispatch() ; subscribe()
### redux 异步操作 redux—thunk
- **原理**:可以让 actions 是一个函数,
- **安装**:npm install redux-thunk —save
- **引入**:src/store/index.js
```js
import { createStore,applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
//第二步创建的
import reducer from './reducer.js';
// 下面两个为了使用redux-devtools这个浏览器插件
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk),
);
const store = createStore( reducer,enhancer );
//不使用浏览器插件的话
//const store = createStore( reducer, applyMiddleware(thunk) );
export default store;
- 使用:src/store/actionCreators.js
1 | import { CHANGE_INPUT_VALUE, GET_HTTP_DATA } from "./actionTypes"; |
redux 异步操作 redux—saga
- 原理:通过捕获,拦截对应的 action,然后调用自己的(异步)方法, 这个方法中再去发起另一个 aciotns 出发 reducer
- 安装:npm install redux—saga —save
引入:src/store/index.js
1
2
3
4
5
6
7
8
9
10
11
12import { createStore, applyMiddleware, compose } from "redux";
import createSagaMiddleware from "redux-saga";
import reducer from "./reducer.js";
//异步操作的集合
import Sagas from "./sagas";
// create the saga middleware
const sagaMiddleware = createSagaMiddleware();
// mount it on the store
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
// run the saga
sagaMiddleware.run(Sagas);
export default store;
src/store/sagas.js
1 | import { takeEvery, put } from 'redux-saga/effects'; |
使用:组件中
1
store.dispatch({ type: GET_HTTP_DATA });
React-redux
安装:
1
npm install react-redux
使用:
provider
1
2
3
4
5
6
7
8
9
10
11
12
13
14import React from "react";
import ReactDOM from "react-dom";
import TodoList from "./TodoList";
import { Provider } from "react-redux";
import store from "./store";
const App = (
<Provider store={store}>
<TodoList />
</Provider>
);
const rootElement = document.getElementById("root");
ReactDOM.render(App, rootElement);connect()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37import React, { Component } from "react";
import { connect } from "react-redux";
import { getInputChangeAction } from "./actionCreators";
class TodoList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div>
<input
value={this.props.inputValue}
onChange={this.props.handleInputChange}
/>
</div>
</div>
);
}
}
/**把store中的state转到组件的props**/
const mapStateToProps = state => {
return {
inputValue: state.inputValue
};
};
/** store.dispatch, props**/
const mapDispatchToProps = dispatch => {
return {
handleInputChange(e) {
const action = getInputChangeAction(e.target.value);
dispatch(action);
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);注意:Provider 提供了 store,里面的组件才可以使用 connect()
OPT
项目变大的时候所有的代码逻辑都放入到单个 reducer 函数中都将会让程序变得不可维护,所以需要拆分开
查分到每一个组件的上面,每个组件上建立一个 store/reducer.js 文件,合并用到combineReducers
1
2
3
4
5
6
7
8
9
10
11//合并 reducer 代码
import { combineReducers } from 'redux';
// 拆分出来的reducer ,里面的写法和 之前的src/store/reducer.js 一样
import headerReducer form '../common/header/store/reducer';
export default combineReducers({
header: headerReducer
});
// 这个时候组件中的mapstateToprops 要用 state.header.*** 去赋值了数据的 state 有可能不小心被改变了,这个时候可以引入一个immutable 组件,然后用 redux-immutable 去统一数据格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41//安装
npm install immutable --save
// 然后是 reducer.js 文件
import { fromJs } from 'immutable';
const defaultState = fromJs({
inputValue:'',
})
export default (state = defaultState ,action ) => {
switch(action.type){
case 'changeInputValue':
//设置值
return state.set('inputValue',action.value);
break;
}
return state;
}
// 组件中
mapstateToprops=(state)=>{
return {
//没有拆分 reducer 时
inputValue: state.get('inputValue'),
// 拆分reducer时 (前提引入react-immutable)
focuse: state.get('header').get('focuse'),
//或
focuse: state.getInt(['header' , 'focuse']),
}
}
// 安装 react-immutable
npm install react-immutable
// 组件的reducer 中 (下面的引入改变)
import { combineReducers } from 'redux-immutable';
// 拆分出来的reducer ,里面的写法和 之前的src/store/reducer.js 一样
import headerReducer form '../common/header/store/reducer';
export default combineReducers({
header: headerReducer
});
react-router
### 安装:
1 | npm run react-router-dom |
使用:
1 | import React, { Component } from "react"; |
api 文档 :
https://blog.csdn.net/debbyDeng/article/details/84555817
Route: 最重要的模块,主要职责是当 location 匹配路由时,会将 UI render 出来
Link: 进入页面路由的链接
Router: 所有路由组件最底层的接口,
redirect:跳一个新路由,新 location 将覆盖 history stack 中的当前 location
NavLink: link 的特殊版本,当匹配当前 url 时,会给当前 Link 添加样式
prompt: 阻止用户跳转路由时的提示,配合 router 的 getUserConfirmation 使用
-
- MemoryTouter:把 url 保存在内存中,RN,测试等使用
##